package com.duguo.dynamicmvc.factory.utils.javassist;

import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.List;

import javassist.CtClass;
import javassist.CtMethod;
import javassist.CtNewMethod;


public class MethodUtils {


    public static CtMethod findMethod(CtClass targetCtClass, String methodName) throws Exception {
        for (CtMethod ctMethod : targetCtClass.getMethods()) {
            if (ctMethod.getName().equals(methodName))
                return ctMethod;
        }
        return null;
    }


    public static CtMethod findMethod(CtClass targetCtClass, CtClass returnCtClass) throws Exception {
        for (CtMethod ctMethod : targetCtClass.getMethods()) {
            if (ctMethod.getReturnType() == returnCtClass)
                return ctMethod;
        }
        return null;
    }

    public static CtMethod findMethodFromInterface(CtClass targetCtClass, String methodName) throws Exception {
        for (CtClass tempInterface : targetCtClass.getInterfaces()) {
            CtMethod result = findMethod(tempInterface, methodName);
            if (result != null)
                return result;
        }
        return null;
    }

    public static List<CtMethod> findMethodsFromAllInterfaces(CtClass targetCtClass, String methodName) throws Exception {
    	List<CtMethod> methods=new ArrayList<CtMethod>();
    	findMethodsFromAllInterfaces(targetCtClass, methodName,methods);
        return methods;
    }

    public static List<CtMethod> findMethodByPattern(CtClass targetCtClass, String methodPattern) throws Exception {
    	List<CtMethod> methods=new ArrayList<CtMethod>();
    	for (CtMethod ctMethod : targetCtClass.getMethods()) {
            if (ctMethod.getName().matches(methodPattern))
            	methods.add(ctMethod);
        }
        return methods;
    }


    public static List<CtMethod> findAbstractMethods(CtClass targetCtClass) throws Exception {
        List<CtMethod> methods=new ArrayList<CtMethod>();
        findAbstractMethods(targetCtClass, methods);
        return methods;
    }

    public static CtMethod addMethod(CtClass targetCtClass, String methodText) throws Exception {
        CtMethod ctMethod = CtNewMethod.make(methodText, targetCtClass);
        targetCtClass.addMethod(ctMethod);
        return ctMethod;
    }

    public static CtMethod addMethodFromAbstractMethod(CtClass targetCtClass, CtMethod abstractCtMethod) throws Exception {
        String bodyText = "{}";
        if (abstractCtMethod.getReturnType() != null) {
            bodyText = "{return null;}";
        }

        CtMethod ctMethod = CtNewMethod.make(Modifier.PUBLIC,
                abstractCtMethod.getReturnType(), abstractCtMethod.getName(),
                abstractCtMethod.getParameterTypes(), abstractCtMethod.getExceptionTypes(),
                bodyText, targetCtClass);
        targetCtClass.addMethod(ctMethod);
        return ctMethod;
    }

    public static CtMethod addAbstractMethodFromMethod(CtClass targetCtClass, CtMethod sourceCtMethod) throws Exception {
        CtMethod ctMethod = CtNewMethod.abstractMethod(sourceCtMethod.getReturnType(), sourceCtMethod.getName(),
                sourceCtMethod.getParameterTypes(), sourceCtMethod.getExceptionTypes(), targetCtClass);
        targetCtClass.addMethod(ctMethod);
        return ctMethod;
    }

    public static CtMethod addParameterToMethod(CtClass targetCtClass, CtMethod originalCtMethod, CtClass newParamType) throws Exception {
        CtClass[] params = new CtClass[originalCtMethod.getParameterTypes().length + 1];
        for (int i = 0; i < originalCtMethod.getParameterTypes().length; i++)
            params[i] = originalCtMethod.getParameterTypes()[i];
        params[originalCtMethod.getParameterTypes().length] = newParamType;
        CtMethod ctMethod = CtNewMethod.abstractMethod(originalCtMethod.getReturnType(), originalCtMethod.getName(),
                params, originalCtMethod.getExceptionTypes(), targetCtClass);
        return ctMethod;
    }

    private static void findMethodsFromAllInterfaces(CtClass targetCtClass, String methodName,List<CtMethod> methods) throws Exception {
    	for (CtClass tempInterface : targetCtClass.getInterfaces()) {
            CtMethod result = findMethod(tempInterface, methodName);
            if (result != null)
            	methods.add(result);
            findMethodsFromAllInterfaces(tempInterface, methodName,methods);
        }
    }



    private static void findAbstractMethods(CtClass targetCtClass, List<CtMethod> methodsList) throws Exception {
        for (CtMethod currentMethod : targetCtClass.getMethods()) {
            if (Modifier.isAbstract(currentMethod.getModifiers())) {
                methodsList.add(currentMethod);
            }
        }
    }

}
